
#include "xdns-host-entry-generator.h"
#include "xdns-data.h"
#include "xdns-decoder.h"
#include "xdns-host-entry.h"
#include "xdns-protocol.h"
#include "xdns-question-info.h"
#include "xdns-resource-record-info.h"
#include <assert.h>

using namespace std;

XdnsError
XdnsHostEntryGenerator::Generate(const struct in_addr* requestedIpAddress,
                                 const string& requestedHostname,
                                 const XdnsQuestionInfo& question,
                                 const XdnsResourceRecordInfo& resourceRecord,
                                 const XdnsData& rawData,
                                 size_t dataOffset,
                                 XdnsHostEntry** hostEntry)
{
    if (NULL == hostEntry)
    {
        return XE_Inval;
    }

    if ((question.m_qclass != XDNS_CLASS_IN) || question.m_qname.empty())
    {
        return XE_NoSupport;
    }

    XdnsError error = XE_Ok;
    switch (question.m_qtype)
    {
    case XDNS_TYPE_A:
    {
        error = GenerateForTypeA(requestedHostname,
                                 question,
                                 resourceRecord,
                                 rawData,
                                 hostEntry);
        break;
    }

    case XDNS_TYPE_PTR:
    {
        error = GenerateForTypePtr(requestedIpAddress,
                                   question,
                                   resourceRecord,
                                   rawData,
                                   dataOffset,
                                   hostEntry);
        break;
    }

    default:
    {
        error = XE_NoSupport;
        break;
    }
    }

    return error;
}

XdnsError
XdnsHostEntryGenerator::GenerateForTypeA(const string& requestedHostname,
                                         const XdnsQuestionInfo& question,
                                         const XdnsResourceRecordInfo& resourceRecord,
                                         const XdnsData& rawData,
                                         XdnsHostEntry** hostEntry)
{
    assert(!(requestedHostname.empty()));
    assert(0 == requestedHostname.compare(question.m_qname));

    if ((resourceRecord.m_type != XDNS_TYPE_A) ||
        (resourceRecord.m_class != XDNS_CLASS_IN) ||
        (requestedHostname.compare(resourceRecord.m_name) != 0))
    {
        return XE_NoSupport;
    }

    size_t addrSize = sizeof(struct in_addr);
    if (resourceRecord.m_rdlength != addrSize)
    {
        return XE_BadResponse;
    }

    struct in_addr address = {0};
    if (rawData.GetData((unsigned char*) (&address),
                        resourceRecord.m_rdataOffset,
                        addrSize) != addrSize)
    {
        return XE_Unexpected;
    }

    XdnsHostEntry* newHostEntry = new (nothrow) XdnsHostEntryImpl(requestedHostname,
                                                                  &address);
    if (NULL == newHostEntry)
    {
        return XE_NoMem;
    }

    *hostEntry = newHostEntry;
    return XE_Ok;
}

XdnsError
XdnsHostEntryGenerator::GenerateForTypePtr(const struct in_addr* requestedIpAddress,
                                           const XdnsQuestionInfo& question,
                                           const XdnsResourceRecordInfo& resourceRecord,
                                           const XdnsData& rawData,
                                           size_t dataOffset,
                                           XdnsHostEntry** hostEntry)
{
    assert(requestedIpAddress != NULL);
    if ((resourceRecord.m_type != XDNS_TYPE_PTR) ||
        (resourceRecord.m_class != XDNS_CLASS_IN) ||
        (question.m_qname.compare(resourceRecord.m_name) != 0))
    {
        return XE_NoSupport;
    }

    string hostname;
    size_t hostnameSize = 0;
    XdnsError error = XdnsDecoder::DecodeLabels(rawData,
                                                dataOffset,
                                                resourceRecord.m_rdataOffset,
                                                hostname,
                                                hostnameSize);
    if (error != XE_Ok)
    {
        return error;
    }

    if (hostname.empty())
    {
        return XE_NoEnt;
    }

    XdnsHostEntry* newHostEntry = new (nothrow) XdnsHostEntryImpl(hostname,
                                                                  requestedIpAddress);
    if (NULL == newHostEntry)
    {
        return XE_NoMem;
    }

    *hostEntry = newHostEntry;
    return XE_Ok;
}

